package animationEngine;

import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * The StringBox class is used to represent the pattern and text Strings on screen to the user
 * 
 * This class handles various methods such as highlighting each character of the text and 
 * chaning such properties as the font size etc.
 * 
 * @author Andrei Palade
 * @author David McBrierty
 * @author Garry Sharp
 * @author Hristo Georgiev
 * @author John Dennis
 * @author Marco Sarconi
 * @version 1.0.1
 */
public class StringBox extends JPanel{

	/** Default generated serial version */
	private static final long serialVersionUID = 1L;

	/** The maxim number of characters to be displayed */
	private int MAX_CHARS_DISPLAYED;

	/** Array of labels used to display characters */
	private JLabel[] charLabel;

	/** The string which is displayed in the boxes */
	public String stringUsed; 

	/** The font size for the text inside text box */
	private float fontSize;

	/** starting index to display text */
	private int currentIndex;	

	/** currently highlighted character in the block*/
	private int currentlyHighlightedChar;


	/**  
	 * Getter for the font size variable
	 * 
	 * @return The size of the current font
	 */
	public float getFontSize() {
		return fontSize;
	}

	
	/**
	 * Setter for the font size variable
	 * It also redraws the text boxes in case that the window is resized
	 * 
	 * @param fontSize Sets a new font size for each label
	 */
	public void setFontSize(float fontSize) {
		this.fontSize = fontSize;
		for  (int i=0;i<charLabel.length;i++)  {
			charLabel[i].setFont(new Font("Courier", Font.PLAIN, (int)fontSize*2));
		}
	}

	
	/**
	 * Getter for the MAX_CHARS_DISPLAYED
	 * 
	 * @return The maximum number of characters to be displayed in a block
	 */
	public int getMAX_CHARS_DISPLAYED() {
		return MAX_CHARS_DISPLAYED;
	}

	
	/**
	 * Setter for the MAX_CHARS_DISPLAYED
	 * 
	 * @param mAX_CHARS_DISPLAYED Sets the number of characters to be displayed
	 */
	public void setMAX_CHARS_DISPLAYED(int mAX_CHARS_DISPLAYED) {
		MAX_CHARS_DISPLAYED = mAX_CHARS_DISPLAYED;
	}


	/**
	 * Getter for stringUsed
	 * 
	 * @return The string used for the current block of labels
	 */
	public String getStringUsed() {
		return stringUsed;
	}

	
	/**
	 * Setter for stringUsed
	 * 
	 * @param str Sets a new string for the block
	 */
	public void setStringUsed(String str) {
		this.stringUsed = str;
	}

	
	/**
	 * @description Constructor 
	 * 
	 * @param string Takes the string that is going to be used to build the block of labels
	 */
	public StringBox(String string) {

		this.currentIndex = 0;
		this.currentlyHighlightedChar = 0;
		this.MAX_CHARS_DISPLAYED = 40;
		this.fontSize = 24;
		setStringUsed(string);
		setupLabels();

		setLayout(new BoxLayout(this,BoxLayout.X_AXIS)); //Each label has it on character

		for(int i = 0; i < charLabel.length; i++)
			
			//For every label thats not null
			if (charLabel[i] != null){
				add(charLabel[i]); //Add it.
			}		
		setBackground(Color.white);
	}


	
	/**
	 * Constructor string and number of characters to be displayed	
	 * 
	 * @param string Takes the string that is going to be used to build the block of labels
	 * @param noOfCharactersDisplayed The number of characters that are going to be displayed 
	 */
	public StringBox(String string, int noOfCharactersDisplayed){

		this.currentIndex = 0;
		this.MAX_CHARS_DISPLAYED = noOfCharactersDisplayed;
		this.fontSize = 24;
		setStringUsed(string);
		setupLabels();

		setLayout(new BoxLayout(this,BoxLayout.X_AXIS)); //Each label has it on character

		for(int i = 0; i < charLabel.length; i++) 
			
			//For every label thats not null
			if (charLabel[i] != null){
				add(charLabel[i]); //Add it.
			}		
		setBackground(Color.white);
	}


	/**
	 * Constructor string and number of characters to be displayed	
	 * 
	 * @param string Takes the string that is going to be used to build the block of labels
	 * @param noOfCharactersDisplayed The number of characters that are going to be displayed
	 * @param fontSize The size of the font that will be used 
	 */
	public StringBox(String string, int noOfCharactersDisplayed, float fontSize){
		this.currentIndex = 0;
		this.MAX_CHARS_DISPLAYED = noOfCharactersDisplayed;
		this.fontSize = fontSize;
		setStringUsed(string);
		setupLabels();

		setLayout(new BoxLayout(this,BoxLayout.X_AXIS)); //Each label has it on character

		for(int i = 0; i < charLabel.length; i++)
			
			//For every label thats not null
			if (charLabel[i] != null){
				add(charLabel[i]); //Add it.
			}		
		
		setBackground(Color.white);
	}

	/**
	 * 
	 * Initialises the labels
	 */
	private void setupLabels(){

		int numberOfCharacters = getStringUsed().length();
		charLabel = new JLabel[MAX_CHARS_DISPLAYED];

		for(int i = currentIndex; i < currentIndex+MAX_CHARS_DISPLAYED; i++){

			if (i > getStringUsed().length()-1)
				break;

			JLabel temp = new JLabel();
			temp.setText(" "+Character.toString(getStringUsed().charAt(i))+" ");
			Font f = new Font("Courier", Font.PLAIN, (int)getFontSize());
			temp.setFont(f);
			temp.setSize(50, 50);
			temp.setOpaque(true);
			temp.setBorder(BorderFactory.createLineBorder(Color.black));
			temp.setBackground(Color.white);
			charLabel[(i-currentIndex)%MAX_CHARS_DISPLAYED] = temp;
		}
		
		//This sets the size of the panel correctly
		if (numberOfCharacters <= MAX_CHARS_DISPLAYED)
			this.setSize(numberOfCharacters*44, 40);
		else
			this.setSize(MAX_CHARS_DISPLAYED*44, 40);

	}

	/**
	 * Reconstructs the block of labels
	 */
	public void reconstruct(){
		this.removeAll();
		setupLabels();

		setLayout(new BoxLayout(this,BoxLayout.X_AXIS)); //Each label has it on character

		for(int i = 0; i < charLabel.length; i++){
			add(charLabel[i]); //Add it.
		}
		
		setBackground(Color.white);
		this.setLocation(0,0);
		this.revalidate();
	}

	
	/**
	 * Increments the current index within the block
	 */
	public void incrementCurrentIndex(){

		if (this.getStringUsed().length() > MAX_CHARS_DISPLAYED){
			this.currentIndex++;

			if (currentIndex + MAX_CHARS_DISPLAYED > this.stringUsed.length())
				currentIndex = this.stringUsed.length() - MAX_CHARS_DISPLAYED;

			this.reconstruct();
		}
	}

	
	/**
	 * Decrements the current index with the block 
	 */
	public void decrementCurrentIndex(){

		if (this.getStringUsed().length() > MAX_CHARS_DISPLAYED){
			this.currentIndex--;

			if (currentIndex < 0)
				currentIndex = 0;

			this.reconstruct();
		}
	}

	
	/**
	 * Resets the current index within the block
	 */
	public void resetCurrentIndex(){
		this.currentIndex = 0;
		this.repaint();
	}
	
	
	/**
	 * This method highlights the given character index to the given colour.
	 * 
	 * @param charIndex The character that is going to be highlighted 
	 * @param color The colour used to highlight
	 */
	public void highlightChar(int charIndex, Color color){
		if (charIndex >= charLabel.length || charLabel[charIndex] == null)
			return;
		//System.out.println("Setting colour of index =" + charIndex);
		charLabel[charIndex].setBackground(color);
		
	}
	
	/**
	 * Set the background colour for each character to the given colour.
	 * 
	 * @param color The colour used to highlight
	 */
	public void setBackgroundColor(Color color){
		for (int i = 0; i < this.stringUsed.length(); i++){
			charLabel[i].setBackground(color);
		}
	}
	
	
	/**
	 * Highlights the labels with the given index with yellow
	 * 
	 * @param charIndex Highlights the matched character
	 */
	public void highlightChar(int charIndex){
		if (charIndex >= charLabel.length || charIndex < 0 || charLabel[charIndex] == null)
			return;
		this.clearHighlightChar(currentlyHighlightedChar);
		charLabel[charIndex].setBackground(Color.yellow);
		currentlyHighlightedChar = charIndex;
	}

	
	/**
	 * Highlights the characters in the block within the specified index if they match
	 * 
	 * @param charIndex The index of the label that is going to be highlighted 
	 * @param matches Highlight only the characters that match
	 */
	public void highlightChar(int charIndex, boolean matches){
		
		if (charIndex >= charLabel.length || charLabel[charIndex] == null)
			return;
		if(matches){
			charLabel[currentlyHighlightedChar].setBackground(Color.green);
		}
		else {
			this.clearHighlightChar(currentlyHighlightedChar);
		}
			charLabel[charIndex].setBackground(Color.yellow);
			currentlyHighlightedChar = charIndex;
	}

	
	/**
	 * Highlights the labels with the given index with red
	 * 
	 * @param charIndex Highlights the unmatched character
	 */
	public void highlightUnmatchedChar(int charIndex){
		charLabel[charIndex].setBackground(Color.red);
		
		for (int i = 0; i < charIndex; i++){
			this.clearHighlightChar(i);
		}
	}

	
	/**
	 * Resets the colour within the block at the specified index
	 * 
	 * @param charIndex Sets the colour white for the given index
	 */
	public void clearHighlightChar(int charIndex){
		charLabel[charIndex].setBackground(Color.white);
	}

	
	/**
	 * Highlights all the labels from the beginning till the specified index of the block 
	 * including the label indicated by the index
	 * 
	 * @param charIndex The index of the label to highlight
	 */
	public void highlightMatchedChar(int charIndex){
		//System.out.print("Calling highlightMatchedChar up to index " + charIndex + ":");
		for (int i = 0; i <= charIndex; i++){
			charLabel[i].setBackground(Color.GREEN);
		}
	}
	
	
	/**
	 * Highlights all the labels from the beginning index till the end of the block 
	 * 
	 * @param charIndex The beginning index to highlight
	 */
	public void highlightMatchedCharForward(int charIndex){
		//System.out.print("Calling highlightMatchedChar up to index " + charIndex + ":");
		for (int i = charIndex; i < this.stringUsed.length(); i++){
			charLabel[i].setBackground(Color.GREEN);
		}
	}

	
	/**
	 * Highlights the matched characters within the specified interval 
	 * (used for brute force)
	 * 
	 * @param startIndex The beginning index in the block
	 * @param endIndex The end index in the block
	 */
	public void highlightMatchedCharBF(int startIndex, int endIndex){
		for (int i = startIndex; i <= endIndex; i++)
			charLabel[i].setBackground(Color.green);
	}
	
	
	/**
	 * Highlights the matched characters within the specified interval
	 * 
	 * @param startIndex The beginning index in the block
	 * @param endIndex The end index in the block
	 */
	public void highlightMatchedChar(int startIndex, int endIndex){
		for (int i = startIndex; i < endIndex; i++)
			charLabel[i].setBackground(Color.green);
	}

	
	/**
	 * Clear the highlighting within the specified interval
	 * 
	 * @param startIndex The beginning of the interval
	 * @param endIndex The end of the interval
	 */
	public void clearHighlightMatchedChar(int startIndex, int endIndex){
		for (int i = startIndex; i < endIndex; i++)
			charLabel[i].setBackground(Color.white);
	}

	
	/**
	 * Resets all the labels till the specified index inside the string box to white 
	 */
	public void clearHighlightMatchedChar(int charIndex){
		for (int i = 0; i < charIndex; i++)
			charLabel[i].setBackground(Color.white);
	}

	
	/**
	 * Resets all the labels inside the string box to white;
	 */
	 public void clearAllHighlighting(){
		 for(int i = 0; i < getStringUsed().length(); i++)
			 charLabel[i].setBackground(Color.white);
	 }

	 
	 /**
	  * Simulates the highlighting of the labels
	  * 
	  * Used during development
	  */
	 public void simulate(){
		 for(int i = 0; i < charLabel.length; i++){
			 highlightChar(i);
			 try {
				 Thread.sleep(100);
			 } catch (InterruptedException e) {
				 e.printStackTrace();
			 }
			 clearHighlightChar(i);
		 }	
	 }

}
